package it.antonio.ilpizze.db.index.btree.file;

import it.antonio.ilpizze.db.index.btree.Entry;
import it.antonio.ilpizze.db.index.btree.LeafNode;
import it.antonio.ilpizze.db.index.utils.FPerst;
import it.antonio.ilpizze.db.index.utils.FilePointer;
import it.antonio.ilpizze.db.io.IOFile;
import it.antonio.ilpizze.func.ComputeIterator;

import java.util.Iterator;

public class FileLeafNode<K extends Comparable<K>, V> implements LeafNode<K, V>, FilePointer, FileBTreeConstants {
	
	/*
	 *  isLeaf|key|size|child_positions|next|prev
	 */
	
	private IOFile file;
	
	private long position;
	private int degree;
	private FPerst<K> keyPersister;
	private FPerst<V> valuePersister;
	
	public FileLeafNode(IOFile file, long position, int degree, FPerst<K> keyPersister, FPerst<V> valuePersister) {
		this.file = file;
		this.position = position;
		this.degree = degree;
		this.keyPersister = keyPersister;
		this.valuePersister = valuePersister;
		
		file.position(position);
		int typeCheck = file.readInt();
		if(typeCheck != LEAF_NODE){
			throw new IllegalStateException("Invalid LeafNode");
		}
	}

	@Override
	public K key() {
		file.position(position + SIZE_INT + SIZE_BOOLEAN);
		K key = keyPersister.read(file);
		return key;
	}

	@Override
	public boolean isLeaf() {
		return true;
	}

	@Override
	public boolean isFull() {
		return size() >= degree;
	}

	@Override
	public boolean isEmpty() {
		return size() == 0;
	}

	@Override
	public int size() {
		file.position(position + SIZE_INT + SIZE_BOOLEAN + keyPersister.space());
		return file.readInt();
	}

	@Override
	public Iterator<Entry<K, V>> iterator() {
		file.position(position + SIZE_INT + SIZE_BOOLEAN + keyPersister.space());
		final int size = file.readInt();
		
		return new ComputeIterator<Entry<K,V>>() {
			
			int count = 0;
			
			@Override
			protected Entry<K, V> computeNext() {
				if(count == size) return finished();
				
				long childPosition = position + SIZE_INT + SIZE_BOOLEAN + keyPersister.space() + SIZE_INT + (count * SIZE_LONG);
				file.position(childPosition);
				long position = file.readLong();
				count++;
				return new FileEntry<K, V>(file, position, keyPersister, valuePersister);
			}

		};
	}

	
	@Override
	public Entry<K, V> get(int i) {
		long childIndex = position + SIZE_INT + SIZE_BOOLEAN + keyPersister.space() + SIZE_INT + (i * SIZE_LONG);
		file.position(childIndex);
		long childPosition = file.readLong();
		
		return new FileEntry<K, V>(file, childPosition, keyPersister, valuePersister);
	}

	@Override
	public void insert(int index, Entry<K, V> entry) {
		FileEntry<K, V> fileEntry = (FileEntry<K, V>) entry;
		
		int size = size();
		
			
		for (int i = size; i > index; i--) {
			file.position(position + SIZE_INT + SIZE_BOOLEAN + keyPersister.space() + SIZE_INT + ((i - 1) * SIZE_LONG));
			long childPos = file.readLong();
			file.writeLong(childPos);
		}
		file.position(position + SIZE_INT + SIZE_BOOLEAN + keyPersister.space()+ SIZE_INT + (index * SIZE_LONG));
		file.writeLong(fileEntry.position());
		file.position(position + SIZE_INT + SIZE_BOOLEAN + keyPersister.space());
		file.writeInt(size + 1);	
		
	}
	
	@Override
	public long position() {
		return position;
	}

	@Override
	public long space() {
		return  SIZE_INT + SIZE_BOOLEAN + keyPersister.space()+ SIZE_INT + (degree * SIZE_LONG) + SIZE_LONG + SIZE_LONG;
	}

	@Override
	public LeafNode<K, V> next() {
		file.position(position + SIZE_INT + SIZE_BOOLEAN + keyPersister.space()+ SIZE_INT + (degree * SIZE_LONG));
		
 		long nextPosition = file.readLong(); 
		if(nextPosition == -1){
			return null;
		} else {
			return new FileLeafNode<K, V>(file, nextPosition, degree, keyPersister, valuePersister);	
		}
	}

	@Override
	public LeafNode<K, V> previous() {
		file.position(position + SIZE_INT + SIZE_BOOLEAN + keyPersister.space()+ SIZE_INT + (degree * SIZE_LONG) + SIZE_LONG);
		
 		long prevPosition = file.readLong(); 
		if(prevPosition == -1){
			return null;
		} else {
			return new FileLeafNode<K, V>(file, prevPosition, degree, keyPersister, valuePersister);	
		}
	}

	@Override
	public void next(LeafNode<K, V> next) {
		FileLeafNode<K, V> fileEntry = (FileLeafNode<K, V>) next;
		file.position(position + SIZE_INT + SIZE_BOOLEAN + keyPersister.space()+ SIZE_INT + (degree * SIZE_LONG));
		file.writeLong(fileEntry.position());
	}

	@Override
	public void previous(LeafNode<K, V> prev) {
		FileLeafNode<K, V> fileEntry = (FileLeafNode<K, V>) prev;
		file.position(position + SIZE_INT + SIZE_BOOLEAN + keyPersister.space()+ SIZE_INT + (degree * SIZE_LONG) + SIZE_LONG);
		file.writeLong(fileEntry.position());
	}

	
}
